

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import javax.swing.*;

/*
 *  The SudokuGame class contains only main method which calls GameFrame class
 *  to create new object of JFrame and then default close operation as "DO_NOTHING_ON_CLOSE".
 *  Finally, make the frame object visible on the screen.
 */
public class SudokuGame {
	public static void main(String[] args){
		

	GameFrame g = new GameFrame();
 //g.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 g.setVisible(true);

	}
}


/*
 * The GameFrame class which extends JFrame class is used to create frame and then add all menus,
 * panels and buttons that will be shown inside the frame.
 *
 */

class GameFrame extends JFrame {
	// constructor
	
	public GameFrame(){
		// get screen dimensions
		Toolkit kit = Toolkit.getDefaultToolkit();
		Dimension screenSize = kit.getScreenSize();
		int screenHeight = screenSize.height;
		int screenWidth = screenSize.width;
		
		
		// set size of frame as 400x400 pixel
		this.setSize(400,400);
		// locate frame in the middle of screen
		Point point = new Point ((screenWidth/2)-(WIDTH/2),(screenHeight/2)-(HEIGHT/2));

		this.setLocation(point);
		
		// set frame title as "Sudoku"
		this.setTitle("Sudoku");
	    	// and not allow frame to be resizable
		this.setResizable(false);
		JFrame mainframe = new JFrame();
		JPanel mypanel = new JPanel();
	    	// add menu bar which already has menus and menu items inside
			// by calling addMenu() method
		
				//mypanel.setJMenuBar(addMenu());
			 addMenu();
	    	// create new panel for label of puzzle's difficulty level
				JPanel diffpanel = new JPanel();
				
	    	// create first JLable object which contains string of "Difficulty Level: "
	    	// and then add it into panel
				JLabel label1 = new JLabel();
				label1.setText("Difficulty Level: ");
				diffpanel.add(label1);
				
	    	// create second JLable object and assign it to private variable
				JLabel label2 = new JLabel();
				
	    	// add second JLable object into panel
				diffpanel.add(label2);
	    	// After that add this new panel into the north region of the frame
				//diffpanel.add(diffpanel,BorderLayout.NORTH);
				mainframe.add(diffpanel,BorderLayout.NORTH);
	    	// create sudoku panel by calling SudokuPanel class
					JPanel sudokupanel = new JPanel();
					//sudokupanel.add(SudokuPanel());
	    	// and then add it into center region of the frame
					//sudokupanel.add(sudokupanel,BorderLayout.CENTER);
					mainframe.add(sudokupanel,BorderLayout.CENTER);
	    	// create hint panel which contains one JButton name "Check"
					JPanel hintpanel = new JPanel();
					
		// create JButton name "Check"
					JButton check = new JButton();
					
		// add action listener by calling checkAnswer() class
					check.addActionListener(new checkAnswer());
		// add check button into panel
					hintpanel.add(check);
		// and then add hint panel into south region of the frame
					//hintpanel.add(hintpanel, BorderLayout.SOUTH);
					mainframe.add(hintpanel, BorderLayout.SOUTH);
	    	// this set of code is used for showing confirmation box when user
	    	// click on closing window icon
	    	this.addWindowListener( new WindowAdapter() {
	    	   public void windowClosing( WindowEvent e ){
	    		   if (JOptionPane.showConfirmDialog(null, "Are you sure ?", 
	    				   "Exit Program", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION) 
	    		   {
	    			   setVisible(false);
	    			   dispose();
    	           }
    	        }
	    	  
	    } ); 
	    // end of closing window
	}
	
	

	

	/*
	 * This private method is used for construct menu bar. It will return 
	 * MenuBar object as Container.
	 */
	@SuppressWarnings("deprecation")
	private JMenuBar addMenu(){
		
		// create menu bar object from JMenuBar
		
		JMenuBar bar = new JMenuBar();
		this.setJMenuBar(bar);
	    // create menus and menu items
	    // create "File" menu and then add it into menu bar
		JMenu File = new JMenu("File");
		bar.add(File);
		
	    // create menu items "New Game", "Load Game", "Save Game" and "Exit" for "File" menu 
	    // 1 create "New Game" menu item from JMenuitem and then add action listener by 
	    // calling GenGame() class. Then add this menu item into "File" menu.
		JMenuItem newgame = new JMenuItem("New Game");
		newgame.addActionListener(new GenGame());
		File.add(newgame);
		
	    	

	    // 2 create "Load Game" menu item and then add action listener by 
	    // calling LoadGame() class. Then add this menu item into "File" menu.
		JMenuItem loadgame = new JMenuItem("Load Game");
		loadgame.addActionListener(new LoadGame());
		File.add(loadgame);


	    // this set of code is use to create text file call "save.txt" under the same 
	    // directory of this java class file. this text will be used to save puzzle.
	    try {
	    	PrintWriter out = new PrintWriter(new FileWriter("save.txt"), true);
	    } catch(IOException e){
	    	e.printStackTrace();
	    }
		// 3 create "Save Game" menu item and then add action listener by 
	    // calling SaveGame() class. Then add this menu item into "File" menu.
	    JMenuItem savegame = new JMenuItem("Save Game");
	    savegame.addActionListener(new SaveGame());
	    File.add(savegame);


	    //set this menu to disable as default when the program first run
	    	savegame.setEnabled(false);
	    // add separator into "File" menu.
	    	
	    // 3 create "Exit" menu item and then add action listener by 
	    // calling ExitProgramAction() class. Then add this menu item into "File" menu.
	    	
	    JMenuItem exit = new JMenuItem("Exit");
	    exit.addActionListener(new ExitProgramAction());
		File.add(exit);

	    
	    // return menu bar object.
			return bar;
	}
	
	

	

	/*
	 * This private class is used for implementing ActionListener. 
	 * When user click on "Edit" menu or closing window icon, the actionPerformed method 
	 * will be invoked and then show dialog box for Confirmation. if user click on "Yes", 
	 * the program in terminated.
	 */
	private class ExitProgramAction implements ActionListener{
		public void actionPerformed(ActionEvent event){
			if (JOptionPane.showConfirmDialog(null, "Are you sure ?", 
					   "Exit Program", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){
				System.exit(0);
		    }
		}
		
	}
	
	/*
	 * This private class is used for implementing ActionListener. 
	 * When user click on Check button, the program will check user answer by comparing 
	 * title's text of JButton objects in puzzleArray, declared as public static ArrayList<JButton> 
	 * in SudokuPanel class, with integer from array of solution. 
	 */
    	private class checkAnswer implements ActionListener{
		public void actionPerformed(ActionEvent event){
			for (int i=0; i<9; i++){
			    for (int j=0;j<9;j++){
			    	int k = (i*9)+j; 
			    	JButton b = SudokuPanel.puzzleArray.get(k);
			    	if(b.getName().equalsIgnoreCase(SudokuPanel.SLOVING_STR)){
			    		if(!b.getText().equals("")){
				    		if(b.getText().equals(new Integer(solution[k]).toString())){
				    			b.setBackground(new Color(208,236,254));
				    		}else{
				    			b.setBackground(Color.RED);
				    		}
			    		}
			    	}
			    }
			}	
		}
	}
    
    	/*
	 * This private class is used for implementing ActionListener. 
	 * When user click on "New Game" menu, the puzzle is generated by calling Generator() class
	 * to create Generator object. After that we can call useful methods to get array of puzzle, 
	 * solution and level of difficulty.
	 */
	private class GenGame implements ActionListener{
		public void actionPerformed(ActionEvent event){
			int difflevel=0;
			// create Generator object
				Generator generator = new Generator(false);
			// then call method createSudoku() to generate puzzle. This method will return "true" if
			// puzzle is successfully generated, or return "false" if it's not 
			if(generator.createSudoku()){ 
			// if puzzle is created successfully then
				 // get level of difficulty
				difflevel = generator.getDifficultyLevel();
			      	 // get working puzzle into array of integer size 81
				puzzle = 	generator.getPuzzle();
			      	 // original puzzle into array of integer size 81
			     original = generator.getPuzzle();
			         // get solved puzzle into array of integer size 81
			      solution = generator.getSolution();
		     	 }
					JLabel difflabel = new JLabel();

			 // after we get level of difficulty, we use switch case to set difficulty label			
				switch (difflevel){
				case 0 : 
					// set label object to show text "EASY"
					difflabel.setText("Difficully Level: EASY");
					
				case 1 : 
					// set label object to show text "MODERATE"
					
					difflabel.setText("Difficully Level: MODERATE");
				case 2 : 
					// set label object to show text "HARD"
					difflabel.setText("Diffucully Level: HARD");
					
				case 3 : 
					// set label object to show text "VERY HARD"

					difflabel.setText("Difficully Level: VERY HARD");
				}
			// update puzzle board with new generated puzzle by calling static method updataTile()
			// in SudokuPanel class. This method need three parameters. 			
			
				SudokuPanel.updateTile(puzzle, original,solution);
			// then set "Save Game" menu to enable for user to save puzzle.
				
				saveGame.setEnabled(true);
		}
	}
	
	/* 
	 * This private class is used for implementing ActionListener. 
	 * When user click on "Save Game" menu, program will open text file called "save.txt" 
	 * and write three set of string (solving puzzle, original puzzle, solution puzzle) into the file.
	 */
	private class SaveGame implements ActionListener{
		public void actionPerformed(ActionEvent event){
			PrintWriter pw;
			try {
				
				// open file using FileWriter and PrintWriter
			//	FileWriter out = new FileWriter("save.txt");
				//PrintWriter out2 = new PrintWriter("save.txt");
				// write string into file. write original puzzle first and then
				// solving puzzle from ArrayList<JButton>, finally solution puzzle
			PrintWriter out = new PrintWriter(new FileWriter("save.txt"),
						true);
				BufferedReader bw = new BufferedReader(new FileReader("save.txt"));
			        pw = new PrintWriter(new FileWriter("save.txt", true));
			        String line = bw.readLine();
			        for(int i=0; i<line.length(); i++)
			        {
			        	pw.println(line.indexOf(i));//not sure
			        }
				pw.flush();
			} catch(IOException e){
				e.printStackTrace();
			}
	
		}
	}

	/* 
	 * This private class is used for implementing ActionListener. 
	 * When user click on "Load Game" menu, program will open text file called "save.txt" 
	 * and read a line of string (solving puzzle, original puzzle, solution puzzle) into the file.
	 */	
	private class LoadGame implements ActionListener{
		public void actionPerformed(ActionEvent event){
			try {
				// read file using FileReader and BufferedReader
				FileReader fr = new FileReader("save.txt");
				BufferedReader in = new BufferedReader(fr);
				String line;
				while ((line = in.readLine()) != null){
					// Parse string into array of integer using stringToPuzzleArray() method
					
				//int intToPuzzleArray = Integer.parseInt(stringToPuzzleArray());
					original = stringToPuzzleArray(line.substring(0,81));
					puzzle = stringToPuzzleArray(line.substring(81, 162));
					solution = stringToPuzzleArray(line.substring(162));
					
					// update puzzle board with loaded puzzle by calling static method updataTile()
					SudokuPanel.updateTile(puzzle, original, solution);
				}
			} catch(IOException e){
				e.printStackTrace();
			}
	
		}
	}
	
	/*
	 * This private method is used for convert ArrayList of puzzle to String
	 */
	private String puzzleArrayListToString(ArrayList<JButton> p){
		String tem = "";
		String text = "";
		for(int i=0; i<p.size(); i++){
			if (p.get(i).getText().equalsIgnoreCase("")){
				text = "0";
			} else {
				text = p.get(i).getText();
			}
			tem = tem + text;
		}
		return tem;
	}

	/*
	 * This private method is used for convert Array of puzzle to String
	 */	
	private String puzzleArrayToString(int[] p){
		String tem = "";
		for(int i=0; i<p.length; i++){
			tem = tem + p[i];
		}
		return tem;
	}

	/*
	 * This private method is used for convert String to Array of integer
	 */	
	private int[] stringToPuzzleArray(String p){
		int[] tem = new int[81];
		for(int i=0; i<p.length(); i++){
			 tem[i] = Integer.parseInt(p.substring(i, i+1));
		}
		return tem;
	}
	
	// set of private variable (properties)
	private final int WIDTH = 400;
	private final int HEIGHT = 400;
	private int[] puzzle = new int[81];
	private int[] original = new int[81];
	private int[] solution = new int[81];
	private int leveldiff;
	private JLabel diffLabel;
	
	private JMenuItem newGame;
	private JMenuItem loadGame;
    private JMenuItem saveGame;
    private JMenuItem exit;
    
}

/*
 * This class which extends JPanel class is used to create puzzle board. The puzzle board is consist of 
 * nine sections which contain a 3x3 tile boards.   
 */
class SudokuPanel extends JPanel{
	// construction
	public SudokuPanel(){
		this.setLayout(new BorderLayout());
		// create new main panel object
			JPanel mainpanel = new JPanel();
		// set layout of main panel as GridLayout 3x3
			mainpanel.setLayout(new GridLayout(3,3,1,2));
	    
	    //crate JButton ArrayList 
	    for (int i=0; i< 81;i++){
	    	// create new JButton with blank title
	    	JButton jbut = new JButton();
	    	// set name of button as "original", we will use name of the JButton object to distinguish 
	    	// puzzle tile and blank tile (user can click on blank tile to add number).
	    	jbut.setName("original");
	    	// add action listener to this JButton object by calling ShowNumberPanelAction() and 
	    	// pass parameter "i" into this constructor.
	    	jbut.addActionListener(new ShowNumPanelAction(i));
	    	// add this JButton into public static ArrayList<JButton> 
	    	 puzzleArray.add(jbut);
		}
	    
	    // after we get ArrayList of JButton object, we built nine 3x3 tile boards and add
	    // them into panel
	    for (int i=0; i<9; i++){
	    	// create new tile panel object
	    	JPanel tile = new JPanel();
	    	// set layout of tile panel as GridLayout 3x3
	    	GridLayout grid = new GridLayout(3,3);
	    	tile.setLayout(grid);
		    for (int j=0;j<9;j++){
		    	int index = (i*9)+j;
		    	// add JButton object from ArrayList<JButton> into tile panel using "index"
		    	// variable to specify ArrayList index.
		    	tile.add(puzzleArray.get(index));
		    }
		    // add each tile panel into main panel
		    mainpanel.add(tile);
	    }
	    // add main panel into center region
	    mainpanel.add(mainpanel,BorderLayout.CENTER);
	}
	
	/*
	 * This public static method is used to update JButton object that contained inside ArrayList<JButton>
	 * according to variables that pass into this method 
	 */
	public static void updateTile(int[] p, int[] o, int[] s){
		
		// assign all passing variables to private static array of integer.
		// The first one is solving puzzle, the second one is original puzzle and
		// the third one is solution puzzle.
		puzzle = p;
		original = o;
		solution = s;
		
		
		// do for loop from 0 to length of array of puzzle
		for (int i=0; i<puzzle.length;i++){
			// if value at index "i" of puzzle array equal to zero, that mean the JButton object 
			// at index "i" in ArrayList<JButton> is blank.
	    	if (puzzle[i]==0){
	    		// set text of JButton object in ArrayList<JButton> at index "i" to blank.
	    				puzzleArray.get(i).setText("");
	    		// set name of JButton object in ArrayList<JButton> at index "i" to "solving".
	    				puzzleArray.get(i).setName("solving");
	    		// set background color of JButton object in ArrayList<JButton> at index "i" as 
	    		// Color(208,236,254).
	    				
	    				puzzleArray.get(i).setBackground(new Color ( 208,236,254 ));
	    	} else {
	    	// if value at index "i" of puzzle array NOT equal to zero, that mean that value could be
	    	// the original puzzle's number that generated from Generator object or number that user
	    	// select from selected number board.
	    		if (original[i] != 0){
	    			// if original puzzle's number at index "i" is NOT zero, that mean the JButton object
	    			// at index "i" in ArrayList<JButton> is the original puzzle.
	    			// then set text of JButton object in ArrayList<JButton> at index "i" to number in 
	    			// original puzzle array at index "i".
	    			puzzleArray.get(i).setText("original :"+(original[i]));
		    		// set name of JButton object in ArrayList<JButton> at index "i" to "original".
	    			puzzleArray.get(i).setName(ORIGINAL_STR);
		    		// set background color of JButton object in ArrayList<JButton> at index "i" as 
		    		// Color(213,250,144).
	    			puzzleArray.get(i).setBackground(new Color(213,250,144));
	    		} else {
	    			// if original puzzle's number at index "i" is zero, that mean the JButton object
	    			// at index "i" in ArrayList<JButton> is the solving puzzle.
	    			// then set text of JButton object in ArrayList<JButton> at index "i" to number in 
	    			// solving puzzle array at index "i".
	    			puzzleArray.get(i).setText(SLOVING_STR+(original[i]));
	    			// set name of JButton object in ArrayList<JButton> at index "i" to "sloving".
	    			puzzleArray.get(i).setName(SLOVING_STR);
	    			// set background color of JButton object in ArrayList<JButton> at index "i" as 
		    		// Color(208,236,254).
	    			puzzleArray.get(i).setBackground(new Color(208,236,254));
	    		}
	    	}
		}
	}
	
	/*
	 * This private method is used to check if user add all the number on the puzzle board.
	 * This method is called every time user choose number on selected number board and it need
	 * solution array as its parameter.
	 */
	private void checkComplete(int[] s){
		// set "completed" variable to true.
		Boolean compleated = true;
		for (int i=0; i<9; i++){
		    for (int j=0;j<9;j++){
		    	int k = (i*9)+j; 
		    	JButton b = puzzleArray.get(k);
		    	// if title's text of JButton object in ArrayList<JButton> at index "k" is blank
		    	if(b.getText().equals("")){
		    		// set "completed" variable to false and then break out the for loop.
		    		compleated = false;
		    		break;
		    		
		    	}else{
		    		// if title's text of JButton object in ArrayList<JButton> at index "k" is NOT equal
		    		// one in solution array at index "k"
		    		
		    		if(!b.getText().equals(s[k])){
		    			// set "completed" variable to false and then break out the for loop.
		    			compleated = false;
		    			break;
		    			
		    		}
		    	}
		    }
	    }
		// if "completed" variable still true.
		if(compleated){
			//call show dialog box and ask user if they need to continue playing game or not
			JOptionPane d = new JOptionPane();
			int answer = d.showConfirmDialog(null, "Well done!!\n" +
					"Do you want to continue?", "Congraturation!!", 
					JOptionPane.YES_NO_OPTION);
			// if answer variable is NOT equal "JOptionPane.YES_OPTION", then terminates program
			if (answer!=JOptionPane.YES_OPTION){
		    	System.exit(0);
		    }
		}
	}

	/* 
	 * This private class is used for implementing ActionListener. 
	 * When user click on each tile (button) on the puzzle board, the selected number board is
	 * shown for user choosing number to add in that specific tile.
	 */
	private class ShowNumPanelAction implements ActionListener{
		// constructor
		public ShowNumPanelAction(int tid){
			this.tileId = tid;
			// get screen dimensions
			Toolkit kit = Toolkit.getDefaultToolkit();
			Dimension screenSize = kit.getScreenSize();
			this.SCREENHEIGHT = screenSize.height;
			this.SCREENWIDTH = screenSize.width;
			
			// crate new JFrame for selected number board
			JFrame  bframe = new JFrame();
			// set size of frame as 200x200 pixel
			 int DEFAULT_WIDTH = 200;
			 int DEFAULT_HEIGHT = 200;
			 bframe.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
			// locate this frame in the middle of the screen
			bframe.setLocation((SCREENWIDTH/2)-(WIDTH/2),(SCREENHEIGHT/2)-(HEIGHT/2));
			// set title of this frame as "Choose Number"
			bframe.setTitle("Choose Number");
			// and not allow frame to be resizable
			bframe.setResizable(false);
			
			// create NumTile panel object
			JPanel NumTile = new JPanel();
			NumTile = new NumTile(bframe,this.tileId);
		}
		
		// this method is run when user click on each solving puzzle tile only. 
		public void actionPerformed(ActionEvent event){
			//check name of JButton object in ArrayList<JButton> at index "tileId" is equal to "solving"
			if (puzzleArray.get(tileId).getName().equals(SLOVING_STR)){
				// then show selected number board frame
				numFrame.setVisible(true);
				// and add NumTile panel object into the selected number board frame
				numFrame.add(numtile);
			}
		}
		
		private final int WIDTH = 200;
		private final int HEIGHT = 200;
		private final int SCREENWIDTH ;
		private final int SCREENHEIGHT ;
		private int tileId;
		private JFrame numFrame;
		private NumTile numtile;
	}
	
	/* 
	 * This private class is used for creating selected number board which contain 9 buttons of number
	 * and a "Cancel" button. When user click on each button, the number indicated on button will be added
	 * into text property of JButton object in ArrayList<JButton> at index "tileId".
	 */
	private class NumTile extends JPanel{
		// constructor
		public NumTile(JFrame pFrame, int bid){
			this.parentFrame = pFrame;
			this.setLayout(new BorderLayout());
			
			// create new JPanel
			JPanel newpanel = new JPanel();
			// set layout of panel as GridLayout of 3x3
			newpanel.setLayout(new GridLayout(3,3));
			
			// call addTile method for each number (1-9)
			addTile(numberPanel, "1", new SelectNumAction(bid, "1"));
			addTile(numberPanel, "2", new SelectNumAction(bid, "2"));
			addTile(numberPanel, "3", new SelectNumAction(bid, "3"));
			addTile(numberPanel, "4", new SelectNumAction(bid, "4"));
			addTile(numberPanel, "5", new SelectNumAction(bid, "5"));
			addTile(numberPanel, "6", new SelectNumAction(bid, "6"));
			addTile(numberPanel, "7", new SelectNumAction(bid, "7"));
			addTile(numberPanel, "8", new SelectNumAction(bid, "8"));
			addTile(numberPanel, "9", new SelectNumAction(bid, "9"));
			
			
			
			
			
			
			
			
			// add panel into the center region of the frame that get from parameter 
			this.parentFrame.add(newpanel,BorderLayout.CENTER);
			// create cancel button
			JButton cancel = new JButton("Cancel");
			// add actionListener 
			cancel.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent event){
					parentFrame.setVisible(false);
					parentFrame.dispose();
				}	
			});
			// add cancel button into the south region of the frame that get from parameter
			this.parentFrame.add(cancel,BorderLayout.SOUTH);
		}
		// this method used to create JButton, add this button into passing Container and add
		// passing actionListener into it.
		private void addTile(Container c, String label, ActionListener listener){
			JButton button = new JButton(label);
			c.add(button);
			button.addActionListener(listener);
		}
		
		/* 
		 * This private class is used for implementing ActionListener. 
		 * When user click on each number button on the selected number board, 
		 * the number indicated on button is added into text property of 
		 * JButton object in ArrayList<JButton> at index "bid".
		 */
		private class SelectNumAction implements ActionListener{
			public SelectNumAction(int bid, String oid){
				this.bid = bid;
				this.oid = oid;
			}
			public void actionPerformed(ActionEvent event){
				puzzleArray.get(bid).setText(oid);
				parentFrame.setVisible(false);
				parentFrame.dispose();
				checkComplete(solution);
			}
			private int bid;
			private String oid;
		}
		
		private JPanel numberPanel;
		private JFrame parentFrame;
		
	}
	
	public static ArrayList<JButton> puzzleArray = new ArrayList<JButton>();
	private static int[] puzzle;
	private static int[] original;
	private static int[] solution;
	public final static String  ORIGINAL_STR = "original";
	public final static String  SLOVING_STR = "solving";
	
}



